package space.misiro.ledgers.keycloak.client.mapper

import org.keycloak.representations.AccessToken
import org.mapstruct.Mapper
import org.mapstruct.Mapping
import org.testcontainers.shaded.org.apache.commons.lang3.time.DateUtils
import space.misiro.ledgers.middleware.api.domain.um.AccessTokenTO
import space.misiro.ledgers.middleware.api.domain.um.BearerTokenTO
import java.util.Date

@Mapper(componentModel = "spring")
interface KeycloakAuthMapper {

    /**
     * 构建承载令牌传输对象
     * @param source Keycloak访问令牌对象
     * @param token 原始令牌字符串
     * @return 包含完整信息的承载令牌传输对象
     *
     * 实现逻辑：
     * 1. 先转换基础令牌信息
     * 2. 计算令牌剩余生存时间（秒）
     * 3. 组装完整承载令牌对象
     */
    fun toBearer(source: AccessToken, token: String): BearerTokenTO {
        val to = toTokenTO(source, token)
        val ttl = (to.exp.time - Date().time) / DateUtils.MILLIS_PER_SECOND
        return BearerTokenTO(token, "Bearer", ttl.toInt(), null, to, to.scopes)
    }

    /**
     * 转换基础访问令牌信息
     * @param source Keycloak访问令牌对象
     * @param token 原始令牌字符串
     * @return 基础访问令牌传输对象
     *
     * 字段映射说明：
     * - 忽略字段：scaId, consent, authorisationId, act
     * - 直接映射字段：exp（过期时间）, jti（JWT ID）, sub（主体）, iat（签发时间）
     * - 特殊映射：
     *   - accessToken 直接使用原始token字符串
     *   - role 映射整个source对象
     *   - tokenUsage 固定为DIRECT_ACCESS类型
     *   - login 使用source的name字段
     */
    @Mapping(target = "scaId", ignore = true)
    @Mapping(target = "consent", ignore = true)
    @Mapping(target = "authorisationId", ignore = true)
    @Mapping(target = "act", ignore = true)
    @Mapping(target = "exp", source = "source.exp")
    @Mapping(target = "jti", source = "source.id")
    @Mapping(target = "sub", source = "source.subject")
    @Mapping(target = "iat", source = "source.iat")
    @Mapping(target = "accessToken", source = "token")
    @Mapping(target = "scopes", source = "source.scope")
    @Mapping(target = "role", source = "source")
    @Mapping(target = "login", source = "source.name")
    @Mapping(
        target = "tokenUsage",
        expression = "java(space.misiro.ledgers.middleware.api.domain.um.TokenUsageTO.DIRECT_ACCESS)"
    )
    fun toTokenTO(source: AccessToken, token: String): AccessTokenTO
}